#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <filesystem>
#include <stdexcept>

namespace fs = std::filesystem;

// 默认分割大小：2000*1000*1000 字节
constexpr std::uintmax_t DEFAULT_CHUNK_SIZE = 2000ULL * 1000ULL * 1000ULL;

// 打印程序使用说明
void printUsage(const std::string& programName) {
    std::cout << "用法: " << std::endl;
    std::cout << "  文件分割: " << programName << " -c <源文件> [分割大小(字节)]" << std::endl;
    std::cout << "  文件合并: " << programName << " -j <输出文件> <文件1> <文件2> [...]" << std::endl;
    std::cout << "  默认分割大小: " << DEFAULT_CHUNK_SIZE << " 字节" << std::endl;
}

// 分割文件
void splitFile(const std::string& inputPath, std::uintmax_t chunkSize) {
    // 检查源文件是否存在
    if (!fs::exists(inputPath)) {
        throw std::runtime_error("源文件不存在: " + inputPath);
    }
    
    // 检查源文件大小
    std::uintmax_t fileSize = fs::file_size(inputPath);
    std::cout << "源文件大小: " << fileSize << " 字节" << std::endl;
    
    // 如果文件大小小于等于分割大小，给出提示不分割
    if (fileSize <= chunkSize) {
        std::cout << "文件大小小于等于指定的分割大小，不进行分割。" << std::endl;
        return;
    }
    
    // 打开源文件
    std::ifstream inputFile(inputPath, std::ios::binary);
    if (!inputFile) {
        throw std::runtime_error("无法打开源文件: " + inputPath);
    }
    
    // 创建目标文件夹（如果需要）
    std::string baseName = fs::path(inputPath).stem().string();
    std::string directory = fs::path(inputPath).parent_path().string();
    if (directory.empty()) {
        directory = ".";
        baseName = inputPath;
    }
    
    // 计算需要分割成多少个文件
    size_t numChunks = (fileSize + chunkSize - 1) / chunkSize;
    std::cout << "将分割成 " << numChunks << " 个文件" << std::endl;
    
    // 分配缓冲区
    std::vector<char> buffer(chunkSize);
    
    // 分割文件
    for (size_t i = 0; i < numChunks; ++i) {
        // 构建输出文件名
        std::string outputFileName = directory + "/" + baseName + ".part" + std::to_string(i + 1);
        
        // 打开输出文件
        std::ofstream outputFile(outputFileName, std::ios::binary);
        if (!outputFile) {
            throw std::runtime_error("无法创建输出文件: " + outputFileName);
        }
        
        // 读取并写入数据
        inputFile.read(buffer.data(), buffer.size());
        std::streamsize bytesRead = inputFile.gcount();
        
        if (bytesRead > 0) {
            outputFile.write(buffer.data(), bytesRead);
            std::cout << "已创建: " << outputFileName << " (" << bytesRead << " 字节)" << std::endl;
        }
        
        outputFile.close();
    }
    
    inputFile.close();
    std::cout << "文件分割完成。" << std::endl;
}

// 合并文件
void joinFiles(const std::string& outputPath, const std::vector<std::string>& inputPaths) {
    // 检查输入文件是否都存在
    for (const auto& path : inputPaths) {
        if (!fs::exists(path)) {
            throw std::runtime_error("输入文件不存在: " + path);
        }
    }
    
    // 创建输出文件
    std::ofstream outputFile(outputPath, std::ios::binary);
    if (!outputFile) {
        throw std::runtime_error("无法创建输出文件: " + outputPath);
    }
    
    // 分配缓冲区（使用8MB缓冲区以提高性能）
    constexpr size_t BUFFER_SIZE = 8 * 1024 * 1024;
    std::vector<char> buffer(BUFFER_SIZE);
    
    // 合并文件
    for (const auto& inputPath : inputPaths) {
        std::cout << "正在合并: " << inputPath << std::endl;
        
        // 打开输入文件
        std::ifstream inputFile(inputPath, std::ios::binary);
        if (!inputFile) {
            throw std::runtime_error("无法打开输入文件: " + inputPath);
        }
        
        // 读取并写入数据
        while (inputFile) {
            inputFile.read(buffer.data(), buffer.size());
            std::streamsize bytesRead = inputFile.gcount();
            
            if (bytesRead > 0) {
                outputFile.write(buffer.data(), bytesRead);
            }
        }
        
        inputFile.close();
    }
    
    outputFile.close();
    std::cout << "文件合并完成。输出文件: " << outputPath << std::endl;
}

int main(int argc, char* argv[]) {
    try {
        // 检查命令行参数
        if (argc < 3) {
            printUsage(argv[0]);
            return 1;
        }
        
        std::string mode = argv[1];
        
        // 处理文件分割模式
        if (mode == "-c") {
            if (argc < 3) {
                printUsage(argv[0]);
                return 1;
            }
            
            std::string inputPath = argv[2];
            std::uintmax_t chunkSize = DEFAULT_CHUNK_SIZE;
            
            // 如果指定了分割大小
            if (argc > 3) {
                try {
                    chunkSize = std::stoull(argv[3]);
                } catch (...) {
                    std::cerr << "无效的分割大小: " << argv[3] << std::endl;
                    printUsage(argv[0]);
                    return 1;
                }
            }
            
            std::cout << "分割模式: " << std::endl;
            std::cout << "源文件: " << inputPath << std::endl;
            std::cout << "分割大小: " << chunkSize << " 字节" << std::endl;
            
            splitFile(inputPath, chunkSize);
        }
        // 处理文件合并模式
        else if (mode == "-j") {
            if (argc < 4) {
                printUsage(argv[0]);
                return 1;
            }
            
            std::string outputPath = argv[2];
            std::vector<std::string> inputPaths;
            
            for (int i = 3; i < argc; ++i) {
                inputPaths.push_back(argv[i]);
            }
            
            std::cout << "合并模式: " << std::endl;
            std::cout << "输出文件: " << outputPath << std::endl;
            std::cout << "输入文件数: " << inputPaths.size() << std::endl;
            
            joinFiles(outputPath, inputPaths);
        }
        // 无效的模式
        else {
            std::cerr << "无效的模式: " << mode << std::endl;
            printUsage(argv[0]);
            return 1;
        }
        
        return 0;
    } catch (const std::exception& e) {
        std::cerr << "错误: " << e.what() << std::endl;
        return 1;
    }
}